<?php
/**
 * Created by PhpStorm.
 * User: plume
 * Date: 2017/11/8
 * Time: 16:17
 */

namespace App\Http\Controllers;

use App\Models\File;
use App\User;
use bandwidthThrottle\BandwidthThrottle;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Models\UserFile;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;

class FileController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    //

    public function uploadAuth(Request $request) {
        $user = Auth::user();
        $file_name = trim($request->input('name'));
        $file_size = intval($request->input('size'));
        $file_hash = $request->input('hash');
        $file_mime = $request->input('mime');
        $parent_folder_id = intval($request->input('parent_folder'));
        $file_ext = "";
        $is_public = intval($request->input('is_public'));

        if($user->role ==  User::ROLE_STUDENT){
            $setting_file_size = \Illuminate\Support\Facades\DB::table('settings')->where("name", "STU_MONOFILE_SIZE")->value('value');
        }elseif($user->role ==  User::ROLE_TEACHER){
            $setting_file_size = \Illuminate\Support\Facades\DB::table('settings')->where("name", "TCH_MONOFILE_SIZE")->value('value');
        }elseif($user->role ==  User::ROLE_OTHER){
            $setting_file_size = \Illuminate\Support\Facades\DB::table('settings')->where("name", "OTH_MONOFILE_SIZE")->value('value');
        }
        if(empty($file_name) || $file_size<1 || $file_size>($setting_file_size*1024) || empty($file_hash)) {
            return ['code'=>1, "msg"=>"invalid data"];
        }

        preg_match("/^(.*)(\.[a-zA-Z0-9]+)$/", $file_name, $matches);
        if(isset($matches[2])) {
            $file_name = $matches[1];
            $file_ext = $matches[2];
        }

        $parent_folder = DB::table('user_files')
            ->where('id', $parent_folder_id)
            ->where(function($query) use($is_public,$user){
                if ($is_public == UserFile::IS_NOT_PUBLIC) {
                    $query->where('user_id', $user->id);
                }
            })
            ->where('status', UserFile::STATUS_NORMAL)
            ->where('type', UserFile::TYPE_FOLDER)
            ->first();
        if(empty($parent_folder)) {
            return ['code'=>2, "msg"=>"invalid parent folder"];
        }
        $file_store_size = ceil($file_size/1024.0);//转换成实际体积，即上取整转换单位为KB
        //判断公共存储空间
        $pub_storage = \Illuminate\Support\Facades\DB::table('settings')->where("name", "PUB_STORAGE")->value('value');
        $pub_used_storage = \Illuminate\Support\Facades\DB::table('settings')->where("name", "PUB_USED_STORAGE")->value('value');
        if($file_store_size+$user->used_storage>$user->total_storage || $file_store_size+$pub_used_storage>$pub_storage) {
            return ['code'=>3, "msg"=>"have no enough space"];
        }
        $file_name_repeat = DB::table('user_files')
            ->where(function($query) use($is_public,$user){
                if ($is_public == UserFile::IS_NOT_PUBLIC) {
                    $query->where('user_id', $user->id);
                }
            })
            ->where('parent_id', $parent_folder->id)
            ->where('status', UserFile::STATUS_NORMAL)
            ->where('type', UserFile::TYPE_FILE)
            ->where('name', $file_name)
            ->where('file_ext', $file_ext)
            ->count();
        if($file_name_repeat>0) {
            return ['code'=>4, "msg"=>"file name repeat"];
        }
        $file_meta = [
            'user_id'=>$user->id,
            'file_name'=>$file_name,
            'file_ext'=>$file_ext,
            'file_size'=>$file_size,
            'file_hash'=>$file_hash,
            'file_mime'=>$file_mime,
            'parent_folder_id'=>$parent_folder->id,
            'is_public'=>$is_public
        ];
        $real_file = DB::table('files')->where('hash', $file_hash)->first();
        if(empty($real_file)) {
            //此文件当前系统中不存在，需要上传
            //ToDo DB LOG
            $upload_token = str_random(16);
            Cache::put($upload_token, json_encode($file_meta), 180);//缓存180分钟
            return ['code'=>0, 'data'=>['upload_token'=>$upload_token]];
        } else {
            //此文件当前系统中已存在，不需要上传
            return $this->uploadSuccess($file_meta, $real_file);
        }
    }

    public function upload(Request $request) {

        // Make sure file is not cached (as it happens for example on iOS devices)
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
        header("Cache-Control: no-store, no-cache, must-revalidate");
        header("Cache-Control: post-check=0, pre-check=0", false);
        header("Pragma: no-cache");

        @set_time_limit(5 * 60);

        $upload_token = $request->input('upload_token');
        $file_meta = Cache::get($upload_token, null);
        if(empty($file_meta)) {
            return response('Invalid Token', 401);
        }
        $file_meta = json_decode($file_meta, true);

        // Settings
        //$targetDir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "netdisk".DIRECTORY_SEPARATOR."u".$file_meta['user_id'];
        $targetDir = storage_path().DIRECTORY_SEPARATOR . "tmp".DIRECTORY_SEPARATOR."u".$file_meta['user_id'];
        $cleanupTargetDir = true; // Remove old files
        $maxFileAge = 5 * 3600; // Temp file age in seconds

        // Create target dir
        if (!file_exists($targetDir)) {
            mkdir($targetDir);
        }

        $file_name = $file_meta['file_hash'];
        $filePath = $targetDir . DIRECTORY_SEPARATOR . $file_name;

        // Chunking might be enabled
        $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
        $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;

        // Remove old temp files
        if ($cleanupTargetDir) {
            if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
                return response('cannot open temp directory', 500);
            }
            while (($file = readdir($dir)) !== false) {
                $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
                // If temp file is current file proceed to the next
                if ($tmpfilePath == "{$filePath}.part") {
                    continue;
                }
                // Remove temp file if it is older than the max age and is not the current file
                if (preg_match('/\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {
                    @unlink($tmpfilePath);
                }
            }
            closedir($dir);
        }

        // Open temp file
        if (!$out = @fopen("{$filePath}.part", $chunk>0&&$chunks>0 ? "ab" : "wb")) {
            return response('Failed to open output stream.', 500);
        }

        if (!empty($_FILES)) {
            if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
                return response('Failed to move uploaded file.', 500);
            }
            // Read binary input stream and append it to temp file
            if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
                return response('Failed to open input stream.', 500);
            }
        } else {
            if (!$in = @fopen("php://input", "rb")) {
                return response('Failed to open input stream.', 500);
            }
        }

        while ($buff = fread($in, 8192)) {
            fwrite($out, $buff);
        }
        @fclose($out);
        @fclose($in);
        // Check if file has been uploaded
        if (!$chunks || $chunk == $chunks - 1) {
            $file_meta['file_size'] = filesize("{$filePath}.part");
            $month = Date('m');
            $day = Date('d');
            $storage_dir = storage_path().DIRECTORY_SEPARATOR . "app". DIRECTORY_SEPARATOR.Date('Y');
            if (!file_exists($storage_dir)) {mkdir($storage_dir);}
            $storage_dir .= DIRECTORY_SEPARATOR.Date('m');
            if (!file_exists($storage_dir)) {mkdir($storage_dir);}
            $storage_dir .= DIRECTORY_SEPARATOR.Date('d');
            if (!file_exists($storage_dir)) {mkdir($storage_dir);}
            rename("{$filePath}.part", $storage_dir.DIRECTORY_SEPARATOR.$file_name);

            $new_file = new File();
            $new_file->name = $file_name;
            $new_file->hash = $file_name;
            $new_file->path = $storage_dir;
            $new_file->mime = $file_meta['file_mime'];
            $new_file->real_size = $file_meta['file_size'];
            $new_file->storage_size = ceil($new_file->real_size / 1024.0);
            $new_file->save();

            return $this->uploadSuccess($file_meta, $new_file);
        } else {
            // Return Success JSON-RPC response
            //die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');
            return response('Success', 200);
        }
    }

    public function uploadSuccess($upload_file_meta, $real_file) {
        $user = User::find($upload_file_meta['user_id']);
        $user_file = new UserFile();
        $user_file->user_id = $user->id;
        $user_file->file_id = $real_file->id;
        $user_file->name = $upload_file_meta['file_name'];
        $user_file->file_ext = $upload_file_meta['file_ext'];
        $user_file->status = UserFile::STATUS_NORMAL;
        $user_file->type = UserFile::TYPE_FILE;
        $user_file->parent_id = $upload_file_meta['parent_folder_id'];
        $user_file->is_public = $upload_file_meta['is_public'];
        $user_file->save();
        if($upload_file_meta['is_public'] == UserFile::IS_PUBLIC){
            $pub_used_storage = \Illuminate\Support\Facades\DB::table('settings')->where("name", "PUB_USED_STORAGE")->value('value');
            \Illuminate\Support\Facades\DB::table('settings')->where("name", "PUB_USED_STORAGE")->update(['value'=> $pub_used_storage+$real_file->storage_size]);
        }else {
            $user->used_storage += $real_file->storage_size;
            $user->save();
        }
        $thumb_path = base_path("/public/thumb/".$real_file->name.".jpg");
        if(in_array($upload_file_meta['file_mime'], ['image/jpeg','image/png']) && !file_exists($thumb_path)) {
            try{
                $this->createThumb($real_file->path, $real_file->name, $upload_file_meta['file_mime']);
            } catch (\Exception $e) {
                //不做处理
            }
        }
        return ['code'=>200, 'msg'=>'success'];
    }

    private function createThumb($file_path, $file_name, $mime) {
        $thumb_dir = base_path().DIRECTORY_SEPARATOR . "public". DIRECTORY_SEPARATOR."thumb";
        if (!file_exists($thumb_dir)) {mkdir($thumb_dir);}
        if ($mime==="image/jpeg")
            $imageTmp=imagecreatefromjpeg($file_path.DIRECTORY_SEPARATOR.$file_name);
        else if ($mime==="image/png")
            $imageTmp=imagecreatefrompng($file_path.DIRECTORY_SEPARATOR.$file_name);
        else
            return false;

        $size = getimagesize($file_path.DIRECTORY_SEPARATOR.$file_name);
        $ratio = $size[0]/$size[1]; // width/height
        if( $ratio > 1) {
            $width = 100;
            $height = 100/$ratio;
        }
        else {
            $width = 100*$ratio;
            $height = 100;
        }
        $dst = imagecreatetruecolor($width,$height);
        imagecopyresampled($dst,$imageTmp,0,0,0,0,$width,$height,$size[0],$size[1]);
        imagejpeg($dst, $thumb_dir.DIRECTORY_SEPARATOR.$file_name.".jpg", 85);
        imagedestroy($dst);
        imagedestroy($imageTmp);
    }

    public function downloadAuth(Request $request, $user_file_id){
        $user = Auth::user();
        $is_public = intval($request->input('is_public'));
        $user_file = UserFile::where('id', $user_file_id)
            ->where(function($query) use($is_public,$user){
                if ($is_public == UserFile::IS_NOT_PUBLIC) {
                    $query->where('user_id', $user->id);
                }
            })
            ->where('status', UserFile::STATUS_NORMAL)
            ->where('type', UserFile::TYPE_FILE)
            ->first();
        if(empty($user_file)) {
            return ['code'=>1, 'msg'=>'文件不存在'];
        }
        $user_file->access_token = str_random(16);
        $user_file->access_token_expire_at = date('Y-m-d H:i:s', time()+3600);
        $user_file->save();
        return ['code'=>0, 'data'=>['access_token'=>$user_file->access_token]];
    }

    public function download($access_token) {
        $user_file = DB::table('user_files')
            ->where('access_token', $access_token)
            ->where('status', UserFile::STATUS_NORMAL)
            ->first();
        if(empty($user_file)) {
            return response(view("errors.error", ['info'=>"文件不存在"]), 400);
        }
        if(!in_array($user_file->type, [UserFile::TYPE_FILE, UserFile::TYPE_SHARE_LINK])) {
            return response(view("errors.error", ['info'=>"文件类型错误"]), 400);
        }
        if($user_file->type==UserFile::TYPE_SHARE_LINK && !($user_file->access_token_expire_at>Date('Y-m-d H:i:s')) ) {
            return response(view("errors.error", ['info'=>"文件已过期"]), 400);
        }
        $real_file = DB::table('files')->where('id', $user_file->file_id)->first();
        if(empty($real_file)) {
            return response(view("errors.error", ['info'=>"文件不存在"]), 400);
        }
        $file = $real_file->path.DIRECTORY_SEPARATOR.$real_file->name;
        if(!file_exists($file)) {
            return response(view("errors.error", ['info'=>"文件不存在"]), 404);
        }

        $user_role = User::where('id', $user_file->user_id)->value('role');
        if($user_role ==  User::ROLE_STUDENT){
            $setting_download_rate = \Illuminate\Support\Facades\DB::table('settings')->where("name", "STU_DOWNLOAD_RATE")->value('value');
        }elseif($user_role ==  User::ROLE_TEACHER){
            $setting_download_rate = \Illuminate\Support\Facades\DB::table('settings')->where("name", "TCH_DOWNLOAD_RATE")->value('value');
        }elseif($user_role ==  User::ROLE_OTHER){
            $setting_download_rate = \Illuminate\Support\Facades\DB::table('settings')->where("name", "OTH_DOWNLOAD_RATE")->value('value');
        }

        $in  = fopen($file, "r");
        $out = fopen("php://output", "w");

        $throttle = new BandwidthThrottle();
        $throttle->setRate($setting_download_rate, BandwidthThrottle::KIBIBYTES); // Set limit to 100KiB/s
        $throttle->throttle($out);

        // Prepare the headers

        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        //'Content-Type'              => \Illuminate\Support\Facades\File::mime(\Illuminate\Support\Facades\File::extension($file)),
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: '.\Illuminate\Support\Facades\File::size($file));
        header('Content-Disposition: attachment; filename="'.$user_file->name.$user_file->file_ext.'"');


        //$response = new Response('', 200, $headers);
        ob_end_clean();
        //$response->send_headers();

        stream_copy_to_stream($in, $out);

        // Finish off, like Laravel would
        //Event::fire('laravel.done', array($response));
        //$response->foundation->finish();

        exit;
    }
}
